home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 1 / Amiga Tools.iso / disk-tools / hd-tools / scsi_tape / source.lha / tape / tape-handler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-16  |  13.1 KB  |  583 lines

  1.  
  2. /*
  3.  *  TAPE-HANDLER.C
  4.  *
  5.  *  tape interface
  6.  *
  7.  *  TAPE:flags
  8.  *
  9.  *     r    rewind
  10.  *     a    append
  11.  *     e    erase - short (may not be supported by drive)
  12.  *     E    erase - long  (erases ENTIRE tape)
  13.  *     <n>    tape buffer, in kilobytes (i.e. tape:4)
  14.  *
  15.  *  To start the handler: (device name defaults to 'scsi.device')
  16.  *
  17.  *  1> run <nil: >nil: l:tape-handler <devicename> [-D<devicename>] -U<unit>
  18.  *  1> run <nil: >nil: l:tape-handler tape -U4
  19.  *
  20.  *  In my example, my tape drive is scsi unit 4.
  21.  *
  22.  *  Note that Append mode is stupid-simple --  I rewind the tape entirely
  23.  *  and then skip file marks until I get a blank-media status.    This is
  24.  *  the ONLY reliable way to 'append' to the commodore tape drive and
  25.  *  should be compatible with ALL tape drives.
  26.  */
  27.  
  28. #include "defs.h"
  29.  
  30. #define SwapBufs(chan) { char *t1 = chan->ch_Buf1; long l1 = chan->ch_BufLen1; \
  31.              chan->ch_Buf1 = chan->ch_Buf2; chan->ch_BufLen1 = chan->ch_BufLen2; \
  32.              chan->ch_Buf2 = t1;  chan->ch_BufLen2 = l1;                 \
  33.             }
  34.  
  35. Prototype int main(short, char **);
  36. Prototype void    myexit(void);
  37. Prototype void    MkDevice(char *);
  38. Prototype void    DelDevice(void);
  39. Prototype void    *DosAllocMem(long);
  40. Prototype void    DosFree(void *);
  41. Prototype void    HandleDosPacket(DosPacket *, short);
  42. Prototype void    HandleReturnedRequest(Chan *);
  43. Prototype void    ReturnPacket(DosPacket *);
  44. Prototype void    HoldPacket(List *, DosPacket *);
  45. Prototype void    RetryWaitingPacket(List *);
  46.  
  47. Prototype MsgPort   *IoSink;
  48. Prototype MsgPort   *DosRetry;
  49. Prototype short     DDebug;
  50. Prototype List        ChanList;
  51.  
  52. MsgPort *IoSink;
  53. MsgPort *DosRetry;
  54. char    *DeviceName = "scsi.device";
  55. long    UnitNo = -1;
  56. short    DDebug;
  57. List    ChanList;
  58. ubyte    TmpBuf[256];
  59.  
  60. __aligned ubyte rewind_cmd[]   = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
  61. __aligned ubyte append_cmd[]   = { 0x11, 0x03, 0x00, 0x00, 0x00, 0x00 };
  62. __aligned ubyte erase_cmd[]    = { 0x19, 0x00, 0x00, 0x00, 0x00, 0x00 };
  63. __aligned ubyte filemark_cmd[] = { 0x10, 0x00, 0x00, 0x00, 0x01, 0x00 };
  64. __aligned ubyte mode_sense_cmd[]={ 0x1A, 0x00, 0x00, 0x00, 0xFF, 0x00 };
  65. __aligned ubyte skip_mark_cmd[] ={ 0x11, 0x01, 0x00, 0x00, 0x01, 0x00 };
  66.  
  67. extern struct DosLibrary *DOSBase;
  68.  
  69. main(ac, av)
  70. short ac;
  71. char *av[];
  72. {
  73.     long sinkMask;
  74.     long retryMask;
  75.     long mask;
  76.  
  77.     atexit(myexit);
  78.     NewList(&ChanList);
  79.     if (ac == 1) {
  80.     printf("tape-handler device_name [-D scsi.device] -U unit\n");
  81.     exit(0);
  82.     }
  83.     IoSink = CreatePort(NULL, 0);
  84.     DosRetry = CreatePort(NULL, 0);
  85.  
  86.     sinkMask = 1 << IoSink->mp_SigBit;
  87.     retryMask = 1 << DosRetry->mp_SigBit;
  88.  
  89.     {
  90.     short i;
  91.     for (i = 2; i < ac; ++i) {
  92.         char *ptr = av[i];
  93.         if (*ptr != '-')
  94.         continue;
  95.         ptr += 2;
  96.         switch(ptr[-1]) {
  97.         case 'D':
  98.         DeviceName = (*ptr) ? ptr : av[++i];
  99.         break;
  100.         case 'U':
  101.         UnitNo = strtol((*ptr) ? ptr : av[++i], NULL, 0);
  102.         break;
  103.         }
  104.     }
  105.     }
  106.     if (DeviceName == NULL || UnitNo == -1) {
  107.     puts("run <nil: >nil: tape-handler <devname> -D<device> -U<unit>");
  108.     exit(20);
  109.     }
  110.     MkDevice(av[1]);
  111.  
  112.     for (;;) {
  113.     Message *msg;
  114.  
  115.     while (msg = GetMsg(DosRetry))
  116.         HandleDosPacket((DosPacket *)msg->mn_Node.ln_Name, 1);
  117.     while (msg = GetMsg(IoSink)) {
  118.         if (msg->mn_Node.ln_Type == NT_MESSAGE)
  119.         HandleDosPacket((DosPacket *)msg->mn_Node.ln_Name, 0);
  120.         else
  121.         HandleReturnedRequest((Chan *)msg->mn_Node.ln_Name);
  122.     }
  123.     mask = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | sinkMask | retryMask);
  124.     if (mask & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) {
  125.         if (GetHead(&ChanList) == NULL)
  126.         break;
  127.     }
  128.     }
  129.     return(0);
  130. }
  131.  
  132. void
  133. myexit()
  134. {
  135.     void *z = NULL;
  136.  
  137.     DelDevice();
  138.     if (IoSink) {
  139.     DeletePort(IoSink);
  140.     IoSink = z;
  141.     }
  142.     if (DosRetry) {
  143.     DeletePort(DosRetry);
  144.     DosRetry = z;
  145.     }
  146. }
  147.  
  148. void
  149. HandleDosPacket(pkt, retry)
  150. DosPacket *pkt;
  151. short retry;
  152. {
  153.     if (retry == 0) {
  154.     pkt->dp_Res1 = 0;
  155.     pkt->dp_Res2 = 0;
  156.     }
  157.  
  158.     switch(pkt->dp_Type) {
  159.     case ACTION_FINDUPDATE:    /*  FileHandle,Lock,Name    Bool        */
  160.     case ACTION_FINDINPUT:    /*  FileHandle,Lock,Name    Bool        */
  161.     case ACTION_FINDOUTPUT:    /*  FileHandle,Lock,Name    Bool        */
  162.     /*
  163.      *  attempt to open tape scsi device
  164.      */
  165.  
  166.     {
  167.         FileHandle *fh = (FileHandle *)BADDR(pkt->dp_Arg1);
  168.         Chan *chan = AllocMem(sizeof(Chan), MEMF_PUBLIC | MEMF_CLEAR);
  169.         int error;
  170.  
  171.         NewList(&chan->ch_PktList);
  172.         {
  173.         ubyte *ptr = (ubyte *)BADDR(pkt->dp_Arg3);
  174.         short len = *ptr;
  175.  
  176.         movmem(ptr + 1, TmpBuf, len);
  177.         TmpBuf[len] = 0;
  178.  
  179.         chan->ch_BufSize = 128 * 1024;
  180.  
  181.         for (ptr = strchr(TmpBuf, ':'); *ptr; ) {
  182.             if (*ptr >= '0' && *ptr <= '9') {
  183.             chan->ch_BufSize = strtol(ptr, &ptr, 0) * 512;
  184.             continue;
  185.             }
  186.             switch(*ptr) {
  187.             case 'a':   /*  append  */
  188.             chan->ch_Flags |= CHANF_APPEND | CHANF_REWIND;
  189.             break;
  190.             case 'r':   /*  rewind  */
  191.             chan->ch_Flags |= CHANF_REWIND;
  192.             break;
  193.             case 'e':   /*  erase   */
  194.             chan->ch_Flags |= CHANF_ERASE;
  195.             break;
  196.             case 'E':   /*  full erase  */
  197.             chan->ch_Flags |= CHANF_ERASE | CHANF_REWIND;
  198.             break;
  199.             }
  200.             ++ptr;
  201.         }
  202.         }
  203.         chan->ch_DeviceName = DeviceName;
  204.         chan->ch_UnitNo    = UnitNo;
  205.         chan->ch_SectSize    = 512;
  206.  
  207.         error = SCSIOpen(chan);
  208.  
  209.         if (error == 0 && chan->ch_Flags & CHANF_REWIND)
  210.         error = DoSCSI(chan, rewind_cmd, NULL, 0, SCSIF_READ, NULL);
  211.  
  212.         if (error == 0 && chan->ch_Flags & CHANF_APPEND) {
  213.         while ((error = DoSCSI(chan, skip_mark_cmd, NULL, 0, SCSIF_READ, NULL)) == 0)
  214.             ;
  215.         if (error == SENSE_BLANK_CHECK)
  216.             error = 0;
  217.         }
  218.  
  219.         if (error == 0 && chan->ch_Flags & CHANF_ERASE) {
  220.         if (chan->ch_Flags & CHANF_REWIND)
  221.             erase_cmd[1] |= 1;
  222.         else
  223.             erase_cmd[1] &= ~1;
  224.         error = DoSCSI(chan, erase_cmd, NULL, 0, SCSIF_READ, NULL);
  225.         }
  226.  
  227.         if (error == 0) {
  228.         long len;
  229.  
  230.         error = DoSCSI(chan, mode_sense_cmd, TmpBuf, sizeof(TmpBuf), SCSIF_READ, &len);
  231.         if (error == 0 && len >= 12)
  232.             chan->ch_SectSize = (TmpBuf[9] << 16) | (TmpBuf[10] << 8) | TmpBuf[11];
  233.         }
  234.         if (chan->ch_BufSize <= 0)
  235.         chan->ch_BufSize = 1;
  236.         chan->ch_BufSize = (chan->ch_BufSize + chan->ch_SectSize - 1) & ~(chan->ch_SectSize - 1);
  237.  
  238.         if ((chan->ch_Buf1 = AllocMem(chan->ch_BufSize, MEMF_PUBLIC)) == NULL)
  239.         error = ERROR_NO_FREE_STORE;
  240.         if ((chan->ch_Buf2 = AllocMem(chan->ch_BufSize, MEMF_PUBLIC)) == NULL)
  241.         error = ERROR_NO_FREE_STORE;
  242.  
  243.         if (error == 0) {
  244.         fh->fh_Port = (MsgPort *)DOS_FALSE;
  245.         fh->fh_Arg1 = chan;
  246.         pkt->dp_Res1 = DOS_TRUE;
  247.         pkt->dp_Res2 = 0;
  248.         AddTail(&ChanList, &chan->ch_Node);
  249.         } else {
  250.         SCSIClose(chan);
  251.         fh->fh_Port = (MsgPort *)DOS_FALSE;
  252.         fh->fh_Arg1 = NULL;
  253.         pkt->dp_Res2 = error;
  254.         pkt->dp_Res1 = DOS_FALSE;
  255.         if (chan->ch_Buf1)
  256.             FreeMem(chan->ch_Buf1, chan->ch_BufSize);
  257.         if (chan->ch_Buf2)
  258.             FreeMem(chan->ch_Buf2, chan->ch_BufSize);
  259.         FreeMem(chan, sizeof(Chan));
  260.         }
  261.     }
  262.     ReturnPacket(pkt);
  263.     break;
  264.     case ACTION_READ:        /*  FHArg1,CPTRBuffer,Length    ActLength   */
  265.     /*
  266.      *  read data
  267.      */
  268.  
  269.     {
  270.         Chan *chan = (Chan *)pkt->dp_Arg1;
  271.         long n;
  272.         long bytes = pkt->dp_Arg3 - pkt->dp_Res1;
  273.  
  274.  
  275.         /*
  276.          *    copy from Buf1
  277.          */
  278.  
  279.         if (n = chan->ch_BufLen1) {
  280.         if (n > bytes)
  281.             n = bytes;
  282.         CopyMem(chan->ch_Buf1 + chan->ch_BufIdx1, (void *)((long)pkt->dp_Arg2 + pkt->dp_Res1), n);
  283.         pkt->dp_Res1 += n;
  284.         bytes         -= n;
  285.         chan->ch_BufLen1 -= n;
  286.         chan->ch_BufIdx1 += n;
  287.         }
  288.  
  289.         /*
  290.          *    return or requeue DOS request as indicated
  291.          */
  292.  
  293.         if (bytes == 0 || (chan->ch_Flags & CHANF_EOF)) {
  294.         if ((chan->ch_Flags & CHANF_ERROR) && pkt->dp_Res1 == 0)
  295.             pkt->dp_Res1 = -1;
  296.         ReturnPacket(pkt);
  297.         } else {
  298.         HoldPacket(&chan->ch_PktList, pkt);
  299.         }
  300.  
  301.         /*
  302.          *    if Buf1 empty and Buf2 not, move'm in
  303.          */
  304.  
  305.         if (chan->ch_BufLen1 == 0 && chan->ch_BufLen2) {
  306.         SwapBufs(chan);
  307.         chan->ch_BufIdx1 = 0;
  308.         RetryWaitingPacket(&chan->ch_PktList);
  309.         }
  310.  
  311.         /*
  312.          *    attempt to read next buffer in while program processing
  313.          *    first.    Or, if buffer is full, swap into buf1
  314.          */
  315.  
  316.         if ((chan->ch_Flags & (CHANF_EOF|CHANF_IOSIP)) == 0 && chan->ch_BufLen2 == 0) {
  317.         __aligned char read_cmd[6] = { 0x08, 0x01, 0x00, 0x00, 0x00, 0x00 };
  318.  
  319.         *(long *)(read_cmd + 2) = chan->ch_BufSize >> 1;
  320.  
  321.         SendSCSI(chan, read_cmd, chan->ch_Buf2, chan->ch_BufSize, SCSIF_READ);
  322.         }
  323.     }
  324.     break;
  325.     case ACTION_WRITE:
  326.     /*
  327.      *  write data
  328.      */
  329.  
  330.     {
  331.         Chan *chan = (Chan *)pkt->dp_Arg1;
  332.         long n;
  333.         long bytes = pkt->dp_Arg3 - pkt->dp_Res1;
  334.  
  335.         /*
  336.          *    copy as much as possible to Buf1
  337.          */
  338.  
  339.         chan->ch_Flags |= CHANF_WRITE;
  340.         if (n = chan->ch_BufSize - chan->ch_BufLen1) {
  341.         if (n > bytes)
  342.             n = bytes;
  343.         CopyMem((void *)((long)pkt->dp_Arg2 + pkt->dp_Res1), (void *)(chan->ch_Buf1 + chan->ch_BufLen1), n);
  344.         pkt->dp_Res1 += n;
  345.         bytes         -= n;
  346.         chan->ch_BufLen1 += n;
  347.         }
  348.  
  349.         /*
  350.          *    return or requeue DOS request as indicated
  351.          */
  352.  
  353.         if (bytes == 0 || (chan->ch_Flags & CHANF_EOF)) {
  354.         if ((chan->ch_Flags & CHANF_ERROR) && pkt->dp_Res1 == 0)
  355.             pkt->dp_Res1 = -1;
  356.         ReturnPacket(pkt);
  357.         } else {
  358.         HoldPacket(&chan->ch_PktList, pkt);
  359.         }
  360.  
  361.         /*
  362.          *    If buffer is full then transfer to Buf2 and start write
  363.          */
  364.  
  365.         if (chan->ch_BufLen1 == chan->ch_BufSize && chan->ch_BufLen2 == 0) {
  366.         __aligned char write_cmd[6] = { 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00 };
  367.  
  368.         *(long *)(write_cmd + 2) = chan->ch_BufLen1 >> 1;
  369.         SendSCSI(chan, write_cmd, chan->ch_Buf1, chan->ch_BufLen1, SCSIF_WRITE);
  370.         SwapBufs(chan);
  371.         RetryWaitingPacket(&chan->ch_PktList);
  372.         }
  373.     }
  374.     break;
  375.     case ACTION_END:
  376.     {
  377.         Chan *chan = (Chan *)pkt->dp_Arg1;
  378.  
  379.         pkt->dp_Res2 = 0;
  380.         pkt->dp_Res1 = DOS_TRUE;
  381.         ReturnPacket(pkt);
  382.  
  383.         Remove(&chan->ch_Node);
  384.         if (chan->ch_Flags & CHANF_WRITE) {
  385.         __aligned char write_cmd[6] = { 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00 };
  386.  
  387.         if (chan->ch_Flags & CHANF_IOSIP)
  388.             WaitSCSI(chan, NULL);
  389.         if (chan->ch_BufLen1) {
  390.             long extra = chan->ch_SectSize - (chan->ch_BufLen1 & (chan->ch_SectSize - 1));
  391.  
  392.             if (extra == chan->ch_SectSize)
  393.             extra = 0;
  394.             if (extra)
  395.             clrmem(chan->ch_Buf1 + chan->ch_BufLen1, extra);
  396.  
  397.             *(long *)(write_cmd + 2) = (chan->ch_BufLen1 + extra) >> 1;
  398.  
  399.             DoSCSI(chan, write_cmd, chan->ch_Buf1, chan->ch_BufLen1 + extra, SCSIF_WRITE, NULL);
  400.         }
  401.         DoSCSI(chan, filemark_cmd, NULL, 0, SCSIF_READ, NULL);
  402.         }
  403.         SCSIClose(chan);
  404.         FreeMem(chan->ch_Buf1, chan->ch_BufSize);
  405.         FreeMem(chan->ch_Buf2, chan->ch_BufSize);
  406.         FreeMem(chan, sizeof(Chan));
  407.     }
  408.     break;
  409.     case ACTION_DIE:
  410.     default:
  411.     pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
  412.     pkt->dp_Res1 = DOS_FALSE;
  413.     ReturnPacket(pkt);
  414.     break;
  415.     }
  416. }
  417.  
  418. void
  419. HandleReturnedRequest(chan)
  420. Chan *chan;
  421. {
  422.     long len;
  423.     int error;
  424.  
  425.     chan->ch_Flags &= ~CHANF_IOSIP;
  426.     error = WaitSCSI(chan, &len);
  427.  
  428.     if (error)
  429.     chan->ch_Flags |= CHANF_ERROR | CHANF_EOF;
  430.  
  431.     if (chan->ch_Flags & CHANF_WRITE) {
  432.     /*
  433.      *  if write returned buffer space now freed up
  434.      */
  435.  
  436.     chan->ch_BufLen2 = 0;
  437.     RetryWaitingPacket(&chan->ch_PktList);
  438.     } else {
  439.     /*
  440.      *  if read returned, data now available and can queue up another
  441.      *  read, in fact!
  442.      */
  443.  
  444.     switch(error) {
  445.     case 0x80:
  446.         chan->ch_Flags |= CHANF_EOF;
  447.         /* fall through */
  448.     case 0:
  449.         break;
  450.     default:
  451.         len = 0;
  452.         chan->ch_Flags |= CHANF_EOF;
  453.         break;
  454.     }
  455.     chan->ch_BufLen2 = len;
  456.  
  457.     if (chan->ch_BufLen1 == 0 && chan->ch_BufLen2) {
  458.         SwapBufs(chan);
  459.         chan->ch_BufIdx1 = 0;
  460.         RetryWaitingPacket(&chan->ch_PktList);
  461.     }
  462.     RetryWaitingPacket(&chan->ch_PktList);
  463.     }
  464. }
  465.  
  466. DosList *Dl;
  467.  
  468. void
  469. MkDevice(devName)
  470. char *devName;
  471. {
  472.     DosList *dl;
  473.     RootNode *root;
  474.     DosInfo *info;
  475.  
  476.     Dl = dl = (struct DosList *)DosAllocMem(sizeof(struct DosList)+strlen(devName)+2);
  477.     strcpy((char *)(dl+1) + 1, devName);
  478.     *(char *)(dl + 1) = strlen(devName);
  479.     dl->dol_Type = DLT_DEVICE;
  480.     dl->dol_Task = IoSink;
  481.     dl->dol_Name = MKBADDR((char *)(dl+1));
  482.  
  483.     if (DOSBase->dl_lib.lib_Version < 37) {
  484.     Forbid();
  485.     root  = (struct RootNode *)DOSBase->dl_Root;
  486.     info  = (struct DosInfo  *)BADDR(root->rn_Info);
  487.     dl->dol_Next = info->di_DevInfo;
  488.     info->di_DevInfo = MKBADDR(dl);
  489.     Permit();
  490.     } else {
  491.     AddDosEntry(dl);
  492.     }
  493. }
  494.  
  495. void
  496. DelDevice()
  497. {
  498.     DosList *dl;
  499.     DosInfo *info;
  500.     RootNode *root;
  501.     DosList *dls;
  502.     BPTR    *bpp;
  503.  
  504.     if (dl = Dl) {
  505.     if (DOSBase->dl_lib.lib_Version < 37) {
  506.         Forbid();
  507.         root  = (struct RootNode *)DOSBase->dl_Root;
  508.         info  = (struct DosInfo  *)BADDR(root->rn_Info);
  509.  
  510.         for (bpp = &info->di_DevInfo; dls = BADDR((*bpp)); bpp = &dls->dol_Next) {
  511.         if (dls == dl)
  512.             break;
  513.         }
  514.         if (dls == dl) {
  515.         *bpp = dls->dol_Next;
  516.         } else {
  517.         ;
  518.         }
  519.         Permit();
  520.         DosFree(dl);
  521.     } else {
  522.         RemDosEntry(dl);
  523.     }
  524.     Dl = NULL;
  525.     }
  526. }
  527.  
  528. void *
  529. DosAllocMem(bytes)
  530. long bytes;
  531. {
  532.     long *ptr;
  533.  
  534.     bytes += 4;
  535.  
  536.     if (ptr = AllocMem(bytes, MEMF_PUBLIC | MEMF_CLEAR)) {
  537.     *ptr++ = bytes;
  538.     return((void *)ptr);
  539.     }
  540. }
  541.  
  542. void
  543. DosFree(vptr)
  544. void *vptr;
  545. {
  546.     long *ptr = vptr;
  547.     --ptr;
  548.     FreeMem(ptr, *ptr);
  549. }
  550.  
  551. void
  552. ReturnPacket(pkt)
  553. DosPacket *pkt;
  554. {
  555.     Message *msg;
  556.     MsgPort *replyPort;
  557.  
  558.     replyPort     = pkt->dp_Port;
  559.     msg      = pkt->dp_Link;
  560.     pkt->dp_Port = IoSink;
  561.     msg->mn_Node.ln_Name = (char *)pkt;
  562.     PutMsg(replyPort, msg);
  563. }
  564.  
  565. void
  566. HoldPacket(list, pkt)
  567. List *list;
  568. DosPacket *pkt;
  569. {
  570.     AddTail(list, &pkt->dp_Link->mn_Node);
  571. }
  572.  
  573. void
  574. RetryWaitingPacket(list)
  575. List *list;
  576. {
  577.     Message *msg;
  578.  
  579.     while (msg = RemHead(list))
  580.     PutMsg(DosRetry, msg);
  581. }
  582.  
  583.